package hw2.cs236369.filtering;

import hw2.cs236369.extensions.Extender;
import hw2.cs236369.extensions.Function;
import hw2.cs236369.interfaces.IAuthor;
import hw2.cs236369.interfaces.IDBLPInfo;
import hw2.cs236369.interfaces.IPaper;

import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;


class DBLPInfoImpl implements IDBLPInfo {
	private final IAuthor									_author;
	private final Collection<IPaper>						_papers;
	private final Collection<? extends Collection<IPaper>>	_papersPerYear;
	private final Collection<Integer>						_paperCountPerYear;
	private final Collection<Integer>						_paperYears;

	private final double									_averagePapers;
	private final int										_firstYear;
	private final double									_averageCoauthors;
	private final int										_totalNumberOfCoauthors;
	private final int										_lastYear;

	/**
	 * Creates a new instance
	 * @param author The author to filter by
	 * @param papers The author's papers
	 */
	public DBLPInfoImpl(final IAuthor author, final Collection<IPaper> papers) {
		_author = author;
		_papers = papers;

		_papersPerYear = Extender.groupBy(_papers, new Function<IPaper, Integer>() {
			@Override
			public Integer calculate(IPaper element) {
				return element.getPublicationYear();
			}
		});

		_paperYears = Extender.map(_papersPerYear, new Function<Collection<IPaper>, Integer>() {
			@Override
			public Integer calculate(Collection<IPaper> element) {
				return Extender.first(element).getPublicationYear();
			}
		});

		_firstYear = Extender.min(_paperYears).intValue();
		_lastYear = Extender.max(_paperYears).intValue();

		_paperCountPerYear = Extender.map(_papersPerYear, new Function<Collection<IPaper>, Integer>() {
			@Override
			public Integer calculate(Collection<IPaper> element) {
				return element.size();
			}
		});

		_averagePapers = (double)getNumberOfPapers() / (_lastYear - _firstYear + 1);

		_averageCoauthors = Extender.average(Extender.map(_papers, new Function<IPaper, Integer>() {
			@Override
			public Integer calculate(IPaper element) {
				return element.getAuthors().size() - 1; // do not include self;
			}
		}));

		Set<IAuthor> coAuthors = new HashSet<>();
		for (IPaper paper : _papers) {
			coAuthors.addAll(paper.getAuthors());
		}
		_totalNumberOfCoauthors = coAuthors.size() - 1; // do not include self
	}

	@Override
	public IAuthor getAuthor() {
		return _author;
	}

	@Override
	public Collection<IPaper> getPapers() {
		return _papers;
	}

	@Override
	public int getNumberOfPapers() {
		return _papers.size();
	}

	@Override
	public double getAverageNumberOfPapersPerYear() {
		return _averagePapers;
	}

	@Override
	public int getMaxNumberOfPapersPerYear() {
		return Extender.max(_paperCountPerYear).intValue();
	}

	@Override
	public int getFirstPublicationYear() {
		return _firstYear;
	}

	@Override
	public int getLastPublicationYear() {
		return _lastYear;
	}

	@Override
	public double getAverageCoauthorsPerPaper() {
		return _averageCoauthors;
	}

	@Override
	public int getTotalNumberOfCoauthors() {
		return _totalNumberOfCoauthors;
	}

	private void appendedToSB(StringBuilder sb, String fieldName, double value) {
		DecimalFormat formatter = new DecimalFormat("0.0");
		formatter.setRoundingMode(RoundingMode.HALF_UP);
		appendedToSB(sb, fieldName, formatter.format(value));
	}

	private void appendedToSB(StringBuilder sb, @SuppressWarnings("unused") String fieldName, int value) {
		sb.append(value + " ");
	}
	
	private void appendedToSB(StringBuilder sb, @SuppressWarnings("unused") String fieldName, Object value) {
		sb.append(value + " ");
	}

	@Override
	public String toPrintableString() {
		StringBuilder sb = new StringBuilder();
		appendedToSB(sb, "author's name", getAuthor().getName());
		appendedToSB(sb, "total number of papers", getNumberOfPapers());
		appendedToSB(sb, "average number of papers per year", getAverageNumberOfPapersPerYear());
		appendedToSB(sb, "maximum number of papers per year", getMaxNumberOfPapersPerYear());
		appendedToSB(sb, "year of first publication", getFirstPublicationYear());
		appendedToSB(sb, "year of last publication", getLastPublicationYear());
		appendedToSB(sb, "average number of coauthors per publication", getAverageCoauthorsPerPaper());
		appendedToSB(sb, "total number of coauthors this author coauthored with", getTotalNumberOfCoauthors());

		return sb.toString().trim();
	}
}
